package zfind

import (
	"fmt"
	"strings"

	"gitee.com/zevenfly/goutil/zcommon"
)

const (
	STOPMODE_FIND_ONE = iota // 找到一个匹配项就停止
	STOPMODE_FIND_ALL        // 找到所有的匹配项
)

type FileFinder interface {
	Login() error
	HasLogin() bool

	// cmpStatus是目标值和中位素相比较的结果，如果是-1，则表示目标值比中位素小，在中位素左边；反之...
	FindOnce(basePath, exceptFileName string, currentDeepth, stopMode int) (resultPaths, nextDirs []string, cmpStatus int, err error)
}

// 二分法查找；
// 前提是 basePaths 以从小到大的顺序排列。注意，如果 basePaths 没有排序，请在 FindOnce 中返回 0
func binaryFind(ff FileFinder, basePaths []string, exceptFileName string, currentDeepth, stopMode int) (resultPaths []string, nextDirs []string, err error) {
	sid := 0
	eid := len(basePaths) - 1
	mid := (sid + eid) / 2

	for {
		basePath := basePaths[mid]
		basePaths[mid] = "" // 此目录已查过
		//fmt.Println(basePath)
		tresultPaths, tnextDirs, tcmpStatus, terr := ff.FindOnce(basePath, exceptFileName, currentDeepth, stopMode)
		for _, tpath := range tresultPaths {
			resultPath := tpath
			if lowerResultPath := strings.ToLower(resultPath); !strings.HasPrefix(lowerResultPath, "svn://") && !strings.HasPrefix(lowerResultPath, "http://") && !strings.HasPrefix(lowerResultPath, "https://") && !strings.HasPrefix(lowerResultPath, "git://") {
				resultPath = zcommon.PathJoin(basePath, tpath)
			}
			resultPaths = append(resultPaths, resultPath)
		}
		for _, tdir := range tnextDirs {
			nextDirs = append(nextDirs, zcommon.PathJoin(basePath, tdir))
		}
		if terr != nil {
			err = terr
			return
		}

		if len(resultPaths) > 0 && stopMode == STOPMODE_FIND_ONE {
			return
		}

		// 如果找到与目标相近的目录
		if tcmpStatus == 0 {
			break
		} else if tcmpStatus < 0 {
			eid = mid - 1
		} else if tcmpStatus > 0 {
			sid = mid + 1
		}

		// 二分法查找失败
		if sid > eid {
			break
		}
		mid = (sid + eid) / 2
	}

	//fmt.Printf("mid:%d  len:%d\n", mid, len(basePaths))

	// 继续在其余的目录进行检查：由mid向两边查找
	detid := 0
	for {
		detid += 1 // mid已经查找过
		lid := mid - detid
		rid := mid + detid

		if lid < 0 && rid > len(basePaths)-1 {
			break
		}

		ids := make([]int, 0, 200)
		//var ids []int
		if lid >= 0 && basePaths[lid] != "" {
			ids = append(ids, lid)
		}
		if rid < len(basePaths) && basePaths[rid] != "" {
			ids = append(ids, rid)
		}

		for _, id := range ids {
			basePath := basePaths[id]
			basePaths[mid] = ""
			tresultPaths, tnextDirs, _, terr := ff.FindOnce(basePath, exceptFileName, currentDeepth, stopMode)
			for _, tpath := range tresultPaths {
				resultPath := tpath
				if lowerResultPath := strings.ToLower(resultPath); !strings.HasPrefix(lowerResultPath, "svn://") && !strings.HasPrefix(lowerResultPath, "http://") && !strings.HasPrefix(lowerResultPath, "https://") && !strings.HasPrefix(lowerResultPath, "git://") {
					resultPath = zcommon.PathJoin(basePath, tpath)
				}
				resultPaths = append(resultPaths, resultPath)
			}
			for _, tdir := range tnextDirs {
				nextDirs = append(nextDirs, zcommon.PathJoin(basePath, tdir))
			}
			if terr != nil {
				err = terr
				return
			}

			if len(resultPaths) > 0 && stopMode == STOPMODE_FIND_ONE {
				return
			}
		}
	}

	return
}

// 在 basePath 中 查找 exceptFileName 文件；
// 具体的匹配方式，由 FindOnce() 接口来实现
func Find(ff FileFinder, basePath, exceptFileName string, maxDeepth, stopMode int) ([]string, error) {
	if basePath == "" || exceptFileName == "" {
		return nil, fmt.Errorf("param error: basePath or exceptFileName empty")
	}

	//login
	if !ff.HasLogin() {
		if err := ff.Login(); err != nil {
			return nil, err
		}
	}

	result := make([]string, 0, 20)
	basePaths := []string{basePath}
	for i := 1; i <= maxDeepth; i++ { // 注意是从 1 开始计数
		//fmt.Println(basePaths)
		resultPaths, nextDirs, err := binaryFind(ff, basePaths, exceptFileName, i, stopMode)
		result = append(result, resultPaths...)
		if err != nil {
			return result, err
		}
		if len(result) > 0 && stopMode == STOPMODE_FIND_ONE {
			return result, nil
		}

		//todo: 对nextDirs排序
		//

		basePaths = nextDirs
	}

	return result, nil
}
